package com.kamistoat.netty.niochat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * NIO群聊客户端
 */
public class NIOChatClient {
    // 定义相关属性
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 6667;
    // 注意，客户端也设置了一个Selector，因为客户端也要接收服务端转发来的消息。此时将客户端视作服务端，服务端视作客户端
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;

    /**
     * 构造器
     */
    public NIOChatClient() {
        try {
            this.selector = Selector.open();
            // 客户端SocketChannel连接服务器
            this.socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
            this.socketChannel.configureBlocking(false);
            // 将SocketChannel注册到客户端的Selector，且注册的事件类型是 读就绪
            this.socketChannel.register(this.selector, SelectionKey.OP_READ);
            this.username = socketChannel.getLocalAddress().toString().substring(1);
            System.out.println(username + " is OK..");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 向服务端发送消息
     */
    public void sendInfo(String info) {
        info = username + "说: " + info;
        try {
            // 将info包裹进ByteBuffer中然后写入到channel中
            // 这样之后，服务端的channel就会处于 读就绪 状态，服务端监听到后就拿到了客户端发送的消息
            socketChannel.write(ByteBuffer.wrap(info.getBytes(StandardCharsets.UTF_8)));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 接收服务端回复的消息
     */
    public void readInfo() {
        try {
            // 阻塞监听直到服务端转发消息，使得客户端的SocketChannel变为 读就绪。
            // 如果不设置阻塞，则下面需要根据readChannels的值进行判断，从而决定接下来的操作
            int readChannels = selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    socketChannel.read(byteBuffer);
                    System.out.println(new String(byteBuffer.array()).trim());
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 启动客户端
     *
     * @param args
     */
    public static void main(String[] args) {
        NIOChatClient nioChatClient = new NIOChatClient();
        // 使用异步机制，因为客户端接收数据和发生数据显然是异步的

        // 接收数据的异步线程以2秒间隔运行
        Thread readThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        while (true) {
                            nioChatClient.readInfo();
                            try {
                                Thread.currentThread().sleep(2000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                        }
                    }
                }
        );
        readThread.start();

        // 发送数据
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String s = scanner.nextLine();
            nioChatClient.sendInfo(s);
        }
    }

}
